package com.cx.service;

import com.cx.utils.DateTimeUtils;
import com.cx.utils.JSONParseUtil;
import com.cx.utils.ProtoStuffUtil;
import com.cx.utils.SerializeUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import redis.clients.jedis.JedisCommands;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created on 2019-04-18 15:42.
 * @author Alan Shu
 */
public class RedisService extends RedisTransaction {

    /**
     * 删除key
     * @param key
     */
    public static void delete(String key) {
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        redisTemplate.delete(key);
    }

    /**
     * 批量删除 key
     * @param keys
     * http://doc.redisfans.com/key/del.html
     */
    public static void del(Collection<String> keys) {
        valueOperations(String.class).getOperations().delete(keys);
    }

    /**
     * 为给定 key 设置生存时间，当 key 过期时(生存时间为 0 )，它会被自动删除
     * @param key
     * http://doc.redisfans.com/key/expire.html
     */
    public static boolean expire(String key) {
        return expire(key, RedisTemplateAdapter.cacheLevel2Ttl(), TimeUnit.SECONDS);
    }

    /**
     * 为给定 key 设置生存时间，当 key 过期时(生存时间为 0 )，它会被自动删除
     * @param key
     * @param timeout
     * @param unit
     * http://doc.redisfans.com/key/expire.html
     */
    public static boolean expire(String key, long timeout, TimeUnit unit) {
        if (timeout <= 0) {
            return false;
        }
        Boolean result = valueOperations(String.class).getOperations().expire(key, timeout, unit);
        return JSONParseUtil.parse(result);
    }

    /**
     * EXPIREAT 的作用和 EXPIRE 类似，都用于为 key 设置生存时间
     * @param key
     * @param expireDate
     * http://doc.redisfans.com/key/expire.html
     */
    public static boolean expireAt(String key, Date expireDate) {
        long timeout = DateTimeUtils.countDelta(expireDate, new Date());
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 取消过期时间
     * @param key
     * http://doc.redisfans.com/key/del.html
     */
    public static boolean persist(String key) {
        return JSONParseUtil.parse(getRedisTemplate(false).persist(key));
    }

    /**
     * 将 key 改名为 newkey
     * @param oldKey
     * @param newKey
     * http://doc.redisfans.com/key/rename.html
     */
    public static void rename(String oldKey, String newKey) {
        valueOperations(String.class).getOperations().rename(oldKey, newKey);
        expire(newKey);
    }

    /**
     * 当且仅当 newkey 不存在时，将 key 改名为 newkey
     * @param oldKey
     * @param newKey
     * http://doc.redisfans.com/key/renamenx.html
     */
    public static void renameNx(String oldKey, String newKey) {
        valueOperations(String.class).getOperations().renameIfAbsent(oldKey, newKey);
        expire(newKey);
    }

    /**
     * 以秒为单位，返回给定 key 的剩余生存时间(TTL, time to live)
     * @param key
     * http://doc.redisfans.com/key/ttl.html
     */
    public static long ttl(String key) {
        return ttl(key, TimeUnit.SECONDS);
    }

    /**
     * 返回给定 key 的剩余生存时间(TTL, time to live)
     * @param key
     * @param timeUnit
     * http://doc.redisfans.com/key/ttl.html
     */
    public static long ttl(String key, TimeUnit timeUnit) {
        Long result = valueOperations(String.class).getOperations().getExpire(key, timeUnit);
        return JSONParseUtil.parse(result);
    }

    /**
     * 检查给定 key 是否存在
     * @param key
     * http://doc.redisfans.com/key/exists.html
     */
    public static boolean exist(String key) {
        return valueOperations(String.class).getOperations().hasKey(key);
    }

    /**
     * 将字符串值 value 关联到 key，并设置生存时间(如有)
     * @param key
     * @param value
     * http://doc.redisfans.com/string/set.html
     */
    public static void set(String key, Object value) {
        if (RedisTemplateAdapter.cacheLevel2Ttl() > 0) {
            valueOperations(String.class).set(key, value, RedisTemplateAdapter.cacheLevel2Ttl(), TimeUnit.SECONDS);
        } else {
            valueOperations(String.class).set(key, value);
        }
    }

    /**
     * 将 key 的值设为 value，当且仅当 key 不存在，并设置生存时间(原子操作)
     * @param key
     * @param value
     * http://doc.redisfans.com/string/set.html
     */
    public static boolean setNxEx(String key, Object value) {
        if (RedisTemplateAdapter.cacheLevel2Ttl() <= 0 || null == TimeUnit.SECONDS) {
            throw new IllegalArgumentException("Cache Key must have expire time.");
        }

        String result = CacheStringTemplate.valueOperations().getOperations().execute(
                (RedisCallback<String>) connection -> {
                    if (connection.getNativeConnection() instanceof JedisCommands) {
                        JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                        return commands.set(key, JSONParseUtil.jsonSerial(value), "NX", "PX",
                                TimeUnit.SECONDS.toMillis(RedisTemplateAdapter.cacheLevel2Ttl()));
                    } else {
                        throw new IllegalArgumentException("Native connection is not an object of JedisCommands.class.");
                    }
                }
        );

        return null != result && result.toUpperCase().equals("OK");
    }

    /**
     * 返回 key 所关联的字符串值
     * @param key
     * http://doc.redisfans.com/string/get.html
     */
    public static <T> T get(String key, Class<T> clazz) {
        Object result = valueOperations(clazz).get(key);
        if (result == null) {
            return null;
        }
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 将 key 的值设为 value，当且仅当 key 不存在，并设置生存时间(如有)
     * @param key
     * @param value
     * http://doc.redisfans.com/string/setnx.html
     */
    public static boolean setnx(String key, Object value) {
        boolean result = JSONParseUtil.parse(valueOperations(String.class).setIfAbsent(key, value));

        if (result) {
            expire(key);
        }

        return result;
    }

    /**
     * 同时设置一个或多个 key-value， 并设置生存时间(如有)
     * @param map
     * http://doc.redisfans.com/string/mset.html
     */
    public static void mset(Map<String, Object> map) {
        valueOperations(String.class).multiSet(map);
        for (String String : map.keySet()) {
            if (RedisTemplateAdapter.cacheLevel2Ttl() > 0) {
                expire(String);
            }
        }
    }

    /**
     * 返回所有(一个或多个)给定 key 的值
     * @param keys
     * @param clazz
     * http://doc.redisfans.com/string/mget.html
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> mget(Collection<String> keys, Class<?> clazz) {
        List<T> list = valueOperations(clazz).multiGet(keys);
        if (list == null) {
            list = new ArrayList<T>();
        }

        return SerializeUtil.parseList(list, clazz);
    }

    /**
     * 将给定 key 的值设为 value，并设置生存时间(如有)，同时返回 key 的旧值(old value)
     * @param key
     * @param value
     * @param clazz
     * http://doc.redisfans.com/string/getset.html
     */
    @SuppressWarnings("unchecked")
    public static <T> T getSet(String key, Object value, Class<T> clazz) {
        Object result = valueOperations(clazz).getAndSet(key, value);
        expire(key);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 如果 key 已经存在并且是一个字符串， APPEND 命令将 value 追加到 key 原来的值的末尾。 如果 key 不存在， APPEND 就简单地将给定 key 设为 value
     * ，就像执行 SET key value 一样。
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/string/append.html
     * @return 追加 value 之后， key 中字符串的长度
     */
    public static int append(String key, String value, boolean renewExpired) {
        Integer len = valueOperations(String.class).append(key, value);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(len);
    }

    /**
     * 将 key 中储存的数字值增一
     * @param key
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/string/incr.html
     */
    public static long incr(String key, boolean renewExpired) {
        return incrBy(key, 1, renewExpired);
    }

    /**
     * 将 key 所储存的值加上增量 increment
     * @param key
     * @param delta
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/string/incrby.html
     * @return 加上 increment 之后， key 的值
     */
    public static long incrBy(String key, long delta, boolean renewExpired) {
        Long result = valueOperations(String.class).increment(key, delta);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 用 value 参数覆写(overwrite)给定 key 所储存的字符串值，从偏移量 offset 开始
     * @param key
     * @param value
     * @param offset
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/string/setrange.html
     */
    public static void setRange(String key, String value, int offset, boolean renewExpired) {
        valueOperations(String.class).set(key, value, offset);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
    }

    /**
     * 返回 key 所储存的字符串值的长度
     * @param key
     * http://doc.redisfans.com/string/strlen.html
     * @return
     */
    public static long strLen(String key) {
        Long result = valueOperations(String.class).size(key);
        return JSONParseUtil.parse(result);
    }

    /**
     * 对 key 所储存的字符串值，设置或清除指定偏移量上的位(bit)
     * offset 参数必须大于或等于 0 ，小于 2^32
     * 位的设置或清除取决于 value 参数，可以是 0 也可以是 1
     * @param key
     * @param offset
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/string/setbit.html
     * https://segmentfault.com/a/1190000008836975
     * @return
     */
    public static boolean setBit(String key, int offset, boolean value, boolean renewExpired) {
        Boolean result = valueOperations(String.class).setBit(key, offset, value);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 对 key 所储存的字符串值，获取指定偏移量上的位(bit)。
     * @param key
     * @param offset
     * http://doc.redisfans.com/string/getbit.html
     * @return
     */
    public static boolean getBit(String key, int offset) {
        Boolean result = valueOperations(String.class).getBit(key, offset);
        return JSONParseUtil.parse(result);
    }

    /*********************************** Hash ***********************************/
    /**
     * 删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略
     * @param key
     * @param hashKey
     * http://doc.redisfans.com/hash/hdel.html
     * @return
     */
    public static long hdel(String key, String... hashKey) {
        Long result = hashOperations(String.class).delete(key, hashKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 将哈希表 key 中的域 field 的值设为 value
     * @param key
     * @param hashKey
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/hash/hset.html
     */
    public static void hset(String key, String hashKey, Object value, boolean renewExpired) {
        hashOperations(String.class).put(key, hashKey, value);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
    }

    /**
     * 将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在
     * @param key
     * @param hashKey
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/hash/hsetnx.html
     * @return
     */
    public static boolean hsetNx(String key, String hashKey, Object value, boolean renewExpired) {
        boolean result = JSONParseUtil.parse(hashOperations(String.class).putIfAbsent(key, hashKey, value));

        if (result) {
            if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
                expire(key);
            }
        }
        return result;
    }

    /**
     * 返回哈希表 key 中给定域 field 的值
     * @param key
     * @param hashKey
     * @param clazz
     * http://doc.redisfans.com/hash/hget.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T hget(String key, String hashKey, Class<T> clazz) {
        Object result = hashOperations(clazz).get(key, hashKey);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 返回哈希表 key 中，所有的域和值
     * @param key
     * @param clazz
     * http://doc.redisfans.com/hash/hgetall.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Map<String, T> hgetAll(String key, Class<?> clazz) {
        Map<String, T> map = hashOperations(clazz).entries(key);
        if (map == null) {
            map = new HashMap<String, T>();
        }
        return SerializeUtil.parseMap(map, clazz);
    }

    /**
     * 注意** 线上不推荐使用
     * @param key
     * http://doc.redisfans.com/hash/hkeys.html
     * @return 返回哈希表 key 中的所有域
     */
    public static Set<String> hkeys(String key) {
        Set<Object> set = hashOperations(String.class).keys(key);
        if (set == null) {
            set = new HashSet<>();
        }
        Set<String> resultSet = new HashSet<>();
        for (Object obj : set) {
            resultSet.add(obj == null ? null : obj.toString());
        }
        return resultSet;
    }

    /**
     * 返回哈希表 key 中所有域的值
     * @param key
     * @param clazz
     * http://doc.redisfans.com/hash/hvals.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> hvals(String key, Class<T> clazz) {
        List<Object> list = hashOperations(String.class).values(key);
        if (list == null) {
            list = new ArrayList<Object>();
        }
        List<T> resultList = new ArrayList<T>();
        for (Object result : list) {
            resultList.add((T) SerializeUtil.parse(result, clazz));
        }
        return resultList;
    }

    /**
     * 查看哈希表 key 中，给定域 field 是否存在
     * @param key
     * @param hashKey
     * http://doc.redisfans.com/hash/hexists.html
     * @return
     */
    public static boolean hexists(String key, String hashKey) {
        Boolean result = hashOperations(String.class).hasKey(key, hashKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 为哈希表 key 中的域 field 的值加上增量 increment
     * @param key
     * @param hashKey
     * @param delta
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/hash/hincrby.html
     * @return
     */
    public static long hincrby(String key, String hashKey, long delta, boolean renewExpired) {
        Long result = hashOperations(String.class).increment(key, hashKey, delta);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回哈希表 key 中域的数量
     * @param key
     * http://doc.redisfans.com/hash/hlen.html
     * @return
     */
    public static long hlen(String key) {
        Long result = hashOperations(String.class).size(key);
        return JSONParseUtil.parse(result);
    }

    /**
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中
     * @param key
     * @param map
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/hash/hmset.html
     */
    public static void hmset(String key, Map<String, ?> map, boolean renewExpired) {
        hashOperations(String.class).putAll(key, map);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
    }

    /**
     * 返回哈希表 key 中，一个或多个给定域的值
     * @param key
     * @param hashKeys
     * @param clazz
     * http://doc.redisfans.com/hash/hmget.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> hmget(String key, Collection<String> hashKeys, Class<?> clazz) {
        List<T> list = hashOperations(clazz).multiGet(key, hashKeys);
        if (list == null) {
            list = new ArrayList<T>();
        }
        return SerializeUtil.parseList(list, clazz);
    }

    /**
     * 扫描hash结果值
     * @param cacheKey
     * @param options
     * @param clazz
     * http://redisdoc.com/hash/hscan.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Cursor<Map.Entry<String, T>> hscan(String cacheKey, ScanOptions options, Class<T> clazz) {
        Cursor<Map.Entry<String, T>> cursor = hashOperations(clazz).scan(cacheKey, options);
        return cursor;
    }

    /*********************************** List ***********************************/
    /**
     * 返回列表 key 中，下标为 index 的元素
     * @param key
     * @param index
     * @param clazz
     * http://doc.redisfans.com/list/lindex.html
     * @return
     */
    public static <T> T lindex(String key, long index, Class<T> clazz) {
        Object result = listOperations(String.class).index(key, index);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 返回列表 key 的长度
     * @param key
     * http://doc.redisfans.com/list/llen.html
     * @return
     */
    public static long llen(String key) {
        Long result = listOperations(String.class).size(key);
        return JSONParseUtil.parse(result);
    }

    /**
     * 移除并返回列表 key 的头元素
     * @param key
     * @param clazz
     * http://doc.redisfans.com/list/lpop.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T lpop(String key, Class<T> clazz) {
        Object result = listOperations(clazz).leftPop(key);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 将一个值 value 插入到列表 key 的表头
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/lpush.html
     * @return - 执行 LPUSH 命令后，列表的长度
     */
    public static long lpush(String key, Object value, boolean renewExpired) {
        Long result = listOperations(String.class).leftPush(key, value);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 将多个值 value 插入到列表 key 的表头
     * @param key
     * @param values
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/lpush.html
     * @return - 执行 LPUSH 命令后，列表的长度
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static long lpushAll(String key, Collection values, boolean renewExpired) {
        Long result = listOperations(String.class).leftPushAll(key, values);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 将值 value 插入到列表 key 的表头，当且仅当 key 存在并且是一个列表
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/lpushx.html
     * @return - 执行 LPUSH 命令后，列表的长度
     */
    public static long lpushHx(String key, Object value, boolean renewExpired) {
        Long result = listOperations(String.class).leftPushIfPresent(key, value);
        if (renewExpired && JSONParseUtil.parse(result) > 0) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回列表 key 中指定区间内的元素，区间以偏移量 start 和 stop 指定
     * @param key
     * @param start
     * @param clazz
     * http://doc.redisfans.com/list/lrange.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> lrange(String key, long start, long stop, Class<?> clazz) {
        List<T> list = listOperations(clazz).range(key, start, stop);
        if (list == null) {
            return new ArrayList<T>();
        }

        return SerializeUtil.parseList(list, clazz);
    }

    /**
     * 根据参数 count 的值，移除列表中与参数 value 相等的元素
     * @param key
     * @param value
     * @param count
     * http://doc.redisfans.com/list/lrem.html
     * @return - 被移除元素的数量
     */
    public static long lrem(String key, Object value, long count) {
        Long result = listOperations(String.class).remove(key, count, value);
        return JSONParseUtil.parse(result);
    }

    /**
     * 将列表 key 下标为 index 的元素的值设置为 value
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/lset.html
     */
    public static void lset(String key, Object value, long index, boolean renewExpired) {
        listOperations(String.class).set(key, index, value);
        if (renewExpired) {
            expire(key);
        }
    }

    /**
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除
     * @param key
     * @param start
     * @param stop
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/ltrim.html
     */
    public static void ltrim(String key, long start, long stop, boolean renewExpired) {
        listOperations(String.class).trim(key, start, stop);
        if (renewExpired) {
            expire(key);
        }
    }

    /**
     * 移除并返回列表 key 的尾元素。
     * @param key
     * @param clazz
     * http://doc.redisfans.com/list/rpop.html
     * @return - 列表的尾元素
     */
    @SuppressWarnings("unchecked")
    public static <T> T rpop(String key, Class<T> clazz) {
        Object result = listOperations(clazz).rightPop(key);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 命令 RPOPLPUSH 在一个原子时间内，执行以下两个动作：
     * 1： 将列表 source 中的最后一个元素(尾元素)弹出，并返回给客户端
     * 2： 将 source 弹出的元素插入到列表 destination ，作为 destination 列表的的头元素
     * @param sourceKey
     * @param destKey 过期时间
     * @param clazz
     * @param renewExpired 是否更新
     * http://doc.redisfans.com/list/rpoplpush.html
     * @return - 被弹出的元素
     */
    @SuppressWarnings("unchecked")
    public static <T> T rpopLpush(String sourceKey, String destKey, Class<T> clazz, boolean renewExpired) {
        Object result = listOperations(clazz).rightPopAndLeftPush(sourceKey, destKey);
        if (result != null) {
            if (renewExpired || (isExpireable(destKey) && ttl(destKey) == -1)) {
                expire(destKey);
            }
        }
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 将一个值 value 插入到列表 key 的表尾(最右边)
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/rpush.html
     * @param - renewExpired 是否更新过期时间(如有)
     * @return - 执行 RPUSH 操作后，表的长度
     */
    public static long rpush(String key, Object value, boolean renewExpired) {
        Long result = listOperations(String.class).rightPush(key, value);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 将多个值 value 插入到列表 key 的表尾(最右边)
     * @param key
     * @param values
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/rpush.html
     * @return - 执行 RPUSH 操作后，表的长度
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static long rpushAll(String key, Collection values, boolean renewExpired) {
        Long result = listOperations(String.class).rightPushAll(key, values);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 将值 value 插入到列表 key 的表尾，当且仅当 key 存在并且是一个列表
     * @param key
     * @param value
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/list/rpushx.html
     * @return - 执行 RPUSH 操作后，表的长度
     */
    public static long rpushHx(String key, Object value, boolean renewExpired) {
        Long result = listOperations(String.class).rightPushIfPresent(key, value);
        if (renewExpired && JSONParseUtil.parse(result) > 0) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /*********************************** Set ***********************************/

    /**
     * 将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略
     * @param key
     * @param renewExpired 是否更新过期时间(如有)
     * @param values
     * http://doc.redisfans.com/set/sadd.html
     * @return - 被添加到集合中的新元素的数量，不包括被忽略的元素
     */
    public static long sadd(String key, boolean renewExpired, Object... values) {
        if (values.length <= 0) {
            return 0;
        }
        Long result = setOperations(String.class).add(key, values);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回集合 key 的基数
     * @param key
     * http://doc.redisfans.com/set/scard.html
     * @return
     */
    public static long scard(String key) {
        Long result = setOperations(String.class).size(key);
        return JSONParseUtil.parse(result);
    }

    /**
     * 判断 member 元素是否集合 key 的成员
     * @param key
     * @param member
     * http://doc.redisfans.com/set/sismember.html
     * @return
     */
    public static boolean sismember(String key, Object member) {
        Boolean result = setOperations(String.class).isMember(key, member);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回集合 key 中的所有成员
     * @param key
     * @param clazz
     * http://doc.redisfans.com/set/smembers.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> smembers(String key, Class<?> clazz) {
        Set<T> set = setOperations(clazz).members(key);
        if (set == null) {
            return new HashSet<T>();
        }

        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 移除并返回集合中的一个随机元素
     * @param key
     * @param clazz
     * http://doc.redisfans.com/set/spop.html
     * @return - 被移除的随机元素
     */
    @SuppressWarnings("unchecked")
    public static <T> T spop(String key, Class<T> clazz) {
        Object result = setOperations(clazz).pop(key);
        return SerializeUtil.parse(result, clazz);
    }

    /**
     * 移除集合 key 中的一个或多个 member 元素，不存在的 member 元素会被忽略
     * @param key
     * @param members
     * http://doc.redisfans.com/set/srem.html
     * @return - 被成功移除的元素的数量，不包括被忽略的元素
     */
    public static long srem(String key, Object... members) {
        if (members.length <= 0) {
            return 0;
        }
        Long result = setOperations(String.class).remove(key, members);
        return JSONParseUtil.parse(result);
    }

    /**
     * set scan
     * @param cacheKey
     * @param options
     * @param clazz
     * http://redisdoc.com/set/sscan.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Cursor<T> sscan(String cacheKey, ScanOptions options, Class<T> clazz) {
        Cursor<T> cursor = setOperations(clazz).scan(cacheKey, options);
        return cursor;
    }

    /**
     * 计算个集合的全部成员，该集合是所有给定集合的并集, 将结果保存到 destination 集合
     * @param cacheKey
     * @param otherCacheKeys
     * @param destCacheKey
     * http://redisdoc.com/set/sunionstore.html
     * @return
     */
    public static long sunionstore(String cacheKey, Collection<String> otherCacheKeys, String destCacheKey) {
        Long result = setOperations(String.class).unionAndStore(cacheKey, otherCacheKeys, destCacheKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 计算个集合的全部成员，该集合是所有给定集合的并集, 将结果保存到 destination 集合
     * @param cacheKey
     * @param otherCacheKey
     * @param destCacheKey
     * http://redisdoc.com/set/sunionstore.html
     * @return
     */
    public static long sunionstore(String cacheKey, String otherCacheKey, String destCacheKey) {
        Long result = setOperations(String.class).unionAndStore(cacheKey, otherCacheKey, destCacheKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的并集
     * @param cacheKey
     * @param otherCacheKeys
     * @param clazz
     * http://redisdoc.com/set/sunion.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sunion(String cacheKey, Collection<String> otherCacheKeys, Class<?> clazz) {
        Set<T> set = setOperations(clazz).union(cacheKey, otherCacheKeys);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的并集
     * @param cacheKey
     * @param otherCacheKey
     * @param clazz
     * http://redisdoc.com/set/sunion.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sunion(String cacheKey, String otherCacheKey, Class<?> clazz) {
        Set<T> set = setOperations(clazz).union(cacheKey, otherCacheKey);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }


    /**
     * 将 member 元素从 source 集合移动到 destination 集合
     * @param cacheKey
     * @param value
     * @param destKey
     * http://redisdoc.com/set/smove.html
     * @return
     * 如果 member 元素被成功移除，返回 1 。
     * 如果 member 元素不是 source 集合的成员，并且没有任何操作对 destination 集合执行，那么返回 0
     */
    public static boolean smove(String cacheKey, Object value, String destKey) {
        Boolean result = setOperations(String.class).move(cacheKey, value, destKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * <p>
     * 返回一个集合的全部成员，该集合是所有给定集合的交集
     * @param cacheKey
     * @param otherCacheKeys
     * @param destKey
     * http://redisdoc.com/set/sinter.html
     * @return
     */
    public static long sinterstore(String cacheKey, Collection<String> otherCacheKeys, String destKey) {
        Long result = setOperations(String.class).intersectAndStore(cacheKey, otherCacheKeys, destKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的交集
     * @param cacheKey
     * @param otherCacheKey
     * @param destKey
     * http://redisdoc.com/set/sinter.html
     * @return
     */
    public static long sinterstore(String cacheKey, String otherCacheKey, String destKey) {
        Long result = setOperations(String.class).intersectAndStore(cacheKey, otherCacheKey, destKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的交集
     * @param cacheKey
     * @param otherCacheKeys
     * @param clazz
     * http://redisdoc.com/set/sinter.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sinter(String cacheKey, Collection<String> otherCacheKeys, Class<?> clazz) {
        Set<T> set = setOperations(clazz).intersect(cacheKey, otherCacheKeys);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的交集
     * @param cacheKey
     * @param otherCacheKey
     * @param clazz
     * http://redisdoc.com/set/sinter.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sinter(String cacheKey, String otherCacheKey, Class<?> clazz) {
        Set<T> set = setOperations(clazz).intersect(cacheKey, otherCacheKey);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 计算集合的全部成员，该集合是所有给定集合之间的差集，将结果保存到 destination 集合，而不是简单地返回结果集
     * @param cacheKey
     * @param otherCacheKeys
     * @param destKey
     * http://redisdoc.com/set/sdiffstore.html
     * @return
     */
    public static long sdiffstore(String cacheKey, Collection<String> otherCacheKeys, String destKey) {
        Long result = setOperations(String.class).differenceAndStore(cacheKey, otherCacheKeys, destKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 计算集合的全部成员，该集合是所有给定集合之间的差集，将结果保存到 destination 集合，而不是简单地返回结果集
     * @param cacheKey
     * @param otherCacheKey
     * @param destKey
     * http://redisdoc.com/set/sdiffstore.html
     * @return
     */
    public static long sdiffstore(String cacheKey, String otherCacheKey, String destKey) {
        Long result = setOperations(String.class).differenceAndStore(cacheKey, otherCacheKey, destKey);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合之间的差集
     * @param cacheKey
     * @param otherCacheKeys
     * @param clazz
     * http://redisdoc.com/set/sdiff.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sdiff(String cacheKey, Collection<String> otherCacheKeys, Class<?> clazz) {
        Set<T> set = setOperations(clazz).difference(cacheKey, otherCacheKeys);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * <p>
     * 返回一个集合的全部成员，该集合是所有给定集合之间的差集
     * @param cacheKey
     * @param otherCacheKey
     * @param clazz
     * http://redisdoc.com/set/sdiff.html
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> sdiff(String cacheKey, String otherCacheKey, Class<?> clazz) {
        Set<T> set = setOperations(clazz).difference(cacheKey, otherCacheKey);
        if (set == null) {
            return new HashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /*********************************** SortedSet ***********************************/

    /**
     * 将一个 member 元素及其 score 值加入到有序集 key 当中
     * @param key
     * @param value
     * @param score
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/sorted_set/zadd.html
     * @return
     */
    public static boolean zadd(String key, Object value, double score, boolean renewExpired) {
        Boolean result = zsetOperations(String.class).add(key, value, score);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 将一个或多个 member 元素及其 score 值加入到有序集 key 当中
     * @param key
     * @param tuples
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/sorted_set/zadd.html
     * @return
     */
    public static long zadd(String key, Set<ZSetOperations.TypedTuple<Object>> tuples, boolean renewExpired) {
        Long result = zsetOperations(String.class).add(key, tuples);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回有序集 key 的基数
     * @param key
     * http://doc.redisfans.com/sorted_set/zcard.html
     */
    public static long zcard(String key) {
        Long result = zsetOperations(String.class).size(key);
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回有序集 key 中， score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
     * @param key
     * @param min
     * @param max
     * http://doc.redisfans.com/sorted_set/zcount.html
     */
    public static long zcount(String key, double min, double max) {
        Long result = zsetOperations(String.class).count(key, min, max);
        return JSONParseUtil.parse(result);
    }

    /**
     * <p>
     * 为有序集 key 的成员 member 的 score 值加上增量 increment
     * @param key
     * @param value
     * @param delta
     * @param renewExpired 是否更新过期时间(如有)
     * http://doc.redisfans.com/sorted_set/zincrby.html
     * @return - member 成员的新 score 值
     */
    public static double zincrby(String key, Object value, double delta, boolean renewExpired) {
        Double result = zsetOperations(String.class).incrementScore(key, value, delta);
        if (renewExpired || (isExpireable(key) && ttl(key) == -1)) {
            expire(key);
        }
        return JSONParseUtil.parse(result);
    }

    /**
     * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
     * 如果 member 是有序集 key 的成员，返回 member 的排名。
     * 如果 member 不是有序集 key 的成员，返回 nil 。
     * @param key
     * @param member
     * http://doc.redisfans.com/sorted_set/zrank.html
     * @return
     */
    public static Long zrank(String key, Object member) {
        Long result = zsetOperations(String.class).rank(key, member);
        return result;
    }

    /**
     * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
     * 排名以 0 为底，也就是说， score 值最大的成员排名为 0 。
     * 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。
     * 如果 member 是有序集 key 的成员，返回 member 的排名。
     * 如果 member 不是有序集 key 的成员，返回 nil 。
     * @param key
     * @param member
     * http://doc.redisfans.com/sorted_set/zrevrank.html
     * @return
     */
    public static Long zrevRank(String key, Object member) {
        Long result = zsetOperations(String.class).reverseRank(key, member);
        return result;
    }

    /**
     * 返回有序集 key 中，指定区间内的成员
     * 其中成员的位置按 score 值递增(从小到大)来排序
     * @param key
     * @param start
     * @param stop
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrange.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> zrange(String key, long start, long stop, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).range(key, start, stop);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中，指定区间内的成员 WITHSCORES
     * 其中成员的位置按 score 值递增(从小到大)来排序
     * @param key
     * @param start
     * @param stop
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrange.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<DefaultTypedTuple<T>> zrangeWithScores(String key, long start, long stop, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).rangeWithScores(key, start, stop);
        if (set == null) {
            return new LinkedHashSet<DefaultTypedTuple<T>>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中，指定区间内的成员
     * 其中成员的位置按 score 值递减(从大到小)来排列
     * @param key
     * @param start
     * @param stop
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrange.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> zrevRange(String key, long start, long stop, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).reverseRange(key, start, stop);
        if (set == null) {
            return new LinkedHashSet<T>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中，指定区间内的成员 WITHSCORES
     * 其中成员的位置按 score 值递减(从大到小)来排列
     * @param key
     * @param start
     * @param stop
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrange.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<DefaultTypedTuple<T>> zrevRangeWithScores(String key, long start, long stop, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).reverseRangeWithScores(key, start, stop);
        if (set == null) {
            return new LinkedHashSet<DefaultTypedTuple<T>>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从小到大)的次序排列。
     * 具有相同 score 值的成员按字典序(lexicographical order )排列。
     * @param key
     * @param min
     * @param max
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> zrangeByScore(String key, long min, long max, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).rangeByScore(key, min, max);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从小到大)的次序排列。
     * 具有相同 score 值的成员按字典序(lexicographical order )排列。
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<T> zrangeByScore(String key, long min, long max, long offset, long count, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).rangeByScore(key, min, max, offset, count);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从小到大)的次序排列。
     * WITHSCORES 具有相同 score 值的成员按字典序(lexicographical order )排列。
     * @param key
     * @param min
     * @param max
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    public static <T> Set<DefaultTypedTuple<T>> zrangeByScoreWithScores(String key, long min, long max, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).rangeByScoreWithScores(key, min, max);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从小到大)的次序排列。
     * WITHSCORES 具有相同 score 值的成员按字典序(lexicographical order )排列。
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    public static <T> Set<DefaultTypedTuple<T>> zrangeByScoreWithScores(String key, long min, long max, long offset, long count, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).rangeByScoreWithScores(key, min, max, offset, count);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。
     * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order )排列。
     * 除了成员按 score 值递减的次序排列这一点外， ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一样。
     * @param key
     * @param min
     * @param max
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    public static <T> Set<T> zrevRangeByScore(String key, long min, long max, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).reverseRangeByScore(key, min, max);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。
     * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order )排列。
     * 除了成员按 score 值递减的次序排列这一点外， ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一样。
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    public static <T> Set<T> zrevRangeByScore(String key, long min, long max, long offset, long count, Class<?> clazz) {
        Set<T> set = zsetOperations(clazz).reverseRangeByScore(key, min, max, offset, count);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。
     * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order )排列。
     * 除了成员按 score 值递减的次序排列这一点外， ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一样。
     * @param key
     * @param min
     * @param max
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<DefaultTypedTuple<T>> zrevRangeByScoreWithScores(String key, long min, long max, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).reverseRangeByScoreWithScores(key, min, max);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中， score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。
     * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order )排列。
     * 除了成员按 score 值递减的次序排列这一点外， ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一样。
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @param clazz
     * http://doc.redisfans.com/sorted_set/zrevrangebyscore.html
     * @return {@link LinkedHashSet}
     */
    @SuppressWarnings("unchecked")
    public static <T> Set<DefaultTypedTuple<T>> zrevRangeByScoreWithScores(String key, long min, long max, long offset, long count, Class<?> clazz) {
        Set<DefaultTypedTuple<T>> set = zsetOperations(clazz).reverseRangeByScoreWithScores(key, min, max, offset, count);
        if (set == null) {
            return new LinkedHashSet<>();
        }
        return SerializeUtil.parseTupleSet(set, clazz);
    }

    /**
     * 返回有序集 key 中，成员 member 的 score 值。
     * @param key
     * @param member
     * 如果 member 元素不是有序集 key 的成员，或 key 不存在，返回 nil
     * http://doc.redisfans.com/sorted_set/zscore.html
     * @return
     */
    public static Double zscore(String key, Object member) {
        Double result = zsetOperations(String.class).score(key, member);
        return result;
    }

    /**
     * 移除有序集 key 中的一个或多个成员，不存在的成员将被忽略
     * @param key
     * @param members
     * http://doc.redisfans.com/sorted_set/zrem.html
     * @return
     */
    public static long zrem(String key, Object... members) {
        if (members.length <= 0) {
            return 0;
        }
        Long result = zsetOperations(String.class).remove(key, members);
        return JSONParseUtil.parse(result);
    }

    /**
     * 移除有序集 key 中，指定排名(rank)区间内的所有成员。
     * @param key
     * @param start
     * @param end
     * http://doc.redisfans.com/sorted_set/zremrangebyrank.html
     * @return - 被移除成员的数量
     */
    public static long zremRangeByRank(String key, long start, long end) {
        Long result = zsetOperations(String.class).removeRange(key, start, end);
        return JSONParseUtil.parse(result);
    }

    /**
     * 移除有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
     * @param key
     * @param min
     * @param max
     * http://doc.redisfans.com/sorted_set/zremrangebyscore.html
     * @return
     */
    public static long zremRangeByScore(String key, long min, long max) {
        Long result = zsetOperations(String.class).removeRangeByScore(key, min, max);
        return JSONParseUtil.parse(result);
    }

    public static boolean isExpireable(String key) {
        return RedisTemplateAdapter.cacheLevel2Ttl() > 0;
    }

    /**
     * 获取Redis List 序列化
     * @param key
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> List<T> getListCache(final String key, Class<T> targetClass) {
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.get(key.getBytes());
            }
        });
        if (ArrayUtils.isEmpty(result)) {
            return null;
        }
        return ProtoStuffUtil.deserializeList(result, targetClass);
    }

    /***
     * 将List 放进缓存里面
     * @param key
     * @param objList
     * @param expireTime
     * @param <T>
     * @return
     */
    public static <T> boolean putListCacheWithExpireTime(String key, List<T> objList, final long expireTime) {
        if(CollectionUtils.isEmpty(objList)){
            return false;
        }

        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffUtil.serializeList(objList);
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(bkey, expireTime, bvalue);
                return true;
            }
        });
        return result;
    }

    /***
     * 将List 放进缓存里面
     * @param key
     * @param objList
     * @param <T>
     * @return
     */
    public static <T> boolean putListCache(String key, List<T> objList) {
        if(CollectionUtils.isEmpty(objList)){
            return false;
        }

        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffUtil.serializeList(objList);
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(bkey, bvalue);
                return true;
            }
        });
        return result;
    }

    /**
     * 获取Redis Obj 序列化
     * @param key
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> T getObjCache(final String key, Class<T> targetClass) {
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.get(key.getBytes());
            }
        });
        if (ArrayUtils.isEmpty(result)) {
            return null;
        }
        return ProtoStuffUtil.deserialize(result, targetClass);
    }

    /***
     * Obj 放进缓存里面
     * @param key
     * @param obj
     * @param expireTime
     * @param <T>
     * @return
     */
    public static <T> boolean putObjCacheWithExpireTime(String key, T obj, final long expireTime) {
        if(ObjectUtils.isEmpty(obj)){
            return false;
        }

        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffUtil.serialize(obj);
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(bkey, expireTime, bvalue);
                return true;
            }
        });
        return result;
    }

    /***
     * Obj 放进缓存里面
     * @param key
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> boolean putObjCache(String key, T obj) {
        if(ObjectUtils.isEmpty(obj)){
            return false;
        }

        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffUtil.serialize(obj);
        RedisTemplate<String, String> redisTemplate = getRedisTemplate(true);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(bkey, bvalue);
                return true;
            }
        });
        return result;
    }
}